unit GLGUIInputManager;

interface

{$I GLScene.inc}

uses
  Classes, Windows, Messages, Dialogs, Controls, Forms,
  SysUtils,  opengladapter,
  GLSGenerics,
  GLCrossPlatform,
  GLGUIBase,
  GLGUIMouseControl,
  GLGUIEvent,
  GLKeyBoard,
  GLGUIEventManager;

type
  TGLGUIManagerUsersList = GList<TGLGUIObject>;

  TGLGUIInputManager = class
  private
    FUsers: TGLGUIManagerUsersList;
    FShiftState: TShiftState;
    FDispatchMessages: Boolean;
    procedure HookMessages(var Msg: TMsg; var Handled: Boolean);
  public
    constructor Create;
    procedure PrepareMouseEvent(AEvent: TGLGUIEvent;
      AButton: TGLMouseButton = mbLeft);
    procedure RegisterUser(AUser: TGLGUIObject);
    procedure UnRegisterUser(AUser: TGLGUIObject);
    destructor Destroy; override;
    property ShiftState: TShiftState read FShiftState;
  end;

function GLInputManager: TGLGUIInputManager;

implementation

var
  vGLGUIInputManager: TGLGUIInputManager;

function GLInputManager: TGLGUIInputManager;
begin
  if (vGLGUIInputManager = nil) then
    vGLGUIInputManager := TGLGUIInputManager.Create();
  Result := vGLGUIInputManager;
end;

constructor TGLGUIInputManager.Create;
begin
  Application.OnMessage := HookMessages;

  FUsers := TGLGUIManagerUsersList.Create;
  FShiftState := [];
  FDispatchMessages := True;
end;

procedure TGLGUIInputManager.PrepareMouseEvent(AEvent: TGLGUIEvent;
  AButton: TGLMouseButton = mbLeft);
begin
  with TGLGUIMouseEvent(AEvent) do
  begin
    ShiftState := FShiftState;
    X := GLGUICursor.X;
    Y := GLGUICursor.Y;
    Button := AButton;
    EventPhase := epWorking;
  end;
end;

procedure TGLGUIInputManager.HookMessages(var Msg: TMsg; var Handled: Boolean);
var
  i: integer;
  vEvent: TGLGUIEvent;
begin
  Handled := False;
  if Assigned(Application.MainForm) then
  begin
    case Msg.message of
      WM_LBUTTONDOWN:
        FShiftState := FShiftState + [ssLeft];
      WM_RBUTTONDOWN:
        FShiftState := FShiftState + [ssRight];
      WM_MBUTTONDOWN:
        FShiftState := FShiftState + [ssMiddle];
      WM_LBUTTONUP:
        FShiftState := FShiftState - [ssLeft];
      WM_RBUTTONUP:
        FShiftState := FShiftState - [ssRight];
      WM_MBUTTONUP:
        FShiftState := FShiftState - [ssMiddle];
    end;
    case Msg.message of
      WM_KEYDOWN:
        begin
          for i := 0 to FUsers.Count - 1 do
          begin
            vEvent := KeyDownEvent;
            with TGLGUIKeyEvent(vEvent) do
            begin
              EventPhase := epWorking;
              Capital := Boolean(GetKeyState(VK_CAPITAL));
              if IsKeyDown(VK_SHIFT) then
                Capital := not Capital;
              Key := chr(Msg.wParam);
              Code := Msg.wParam;
            end;
            FUsers.Items[i].DispatchKeyDownEvent();
          end;
        end;
      WM_LBUTTONDOWN:
        begin
          for i := 0 to FUsers.Count - 1 do
          begin
            vEvent := MouseDownEvent;
            PrepareMouseEvent(vEvent, mbLeft);
            FUsers.Items[i].DispatchMouseDownEvent();
          end;
        end;
      WM_RBUTTONDOWN:
        begin
          for i := 0 to FUsers.Count - 1 do
          begin
            vEvent := MouseDownEvent;
            PrepareMouseEvent(vEvent, mbRight);
            FUsers.Items[i].DispatchMouseDownEvent();
          end;
        end;
      WM_MBUTTONDOWN:
        begin
          for i := 0 to FUsers.Count - 1 do
          begin
            vEvent := MouseDownEvent;
            PrepareMouseEvent(vEvent, mbMiddle);
            FUsers.Items[i].DispatchMouseDownEvent();
          end;
        end;
      WM_LBUTTONUP:
        begin
          for i := 0 to FUsers.Count - 1 do
          begin
            vEvent := MouseUpEvent;
            PrepareMouseEvent(vEvent, mbLeft);
            FUsers.Items[i].DispatchMouseUpEvent();
          end;
        end;
      WM_RBUTTONUP:
        begin
          for i := 0 to FUsers.Count - 1 do
          begin
            vEvent := MouseUpEvent;
            PrepareMouseEvent(vEvent, mbRight);
            FUsers.Items[i].DispatchMouseUpEvent();
          end;
        end;
      WM_MBUTTONUP:
        begin
          for i := 0 to FUsers.Count - 1 do
          begin
            vEvent := MouseUpEvent;
            PrepareMouseEvent(vEvent, mbMiddle);
            FUsers.Items[i].DispatchMouseUpEvent();
          end;
        end;
      WM_MOUSEMOVE:
        begin
          for i := 0 to FUsers.Count - 1 do
          begin
            vEvent := MouseMoveEvent;
            PrepareMouseEvent(vEvent);
            FUsers.Items[i].DispatchMouseMoveEvent();
          end;
        end;
      WM_MOUSEWHEEL:
        begin
          for i := 0 to FUsers.Count - 1 do
          begin
            vEvent := MouseWheelEvent;
            with TGLGUIMouseWheelEvent(vEvent) do
            begin
              ShiftState := FShiftState;
              X := GLGUICursor.X;
              Y := GLGUICursor.Y;
              Delta := Msg.wParam div 7864320;
              EventPhase := epWorking;
            end;
            FUsers.Items[i].DispatchMouseWheelEvent();
          end;
        end;
    end;
  end;
end;

procedure TGLGUIInputManager.RegisterUser(AUser: TGLGUIObject);
begin
  FUsers.Add(AUser);
end;

procedure TGLGUIInputManager.UnRegisterUser(AUser: TGLGUIObject);
begin
  FUsers.Remove(AUser);
end;

destructor TGLGUIInputManager.Destroy;
begin
  FUsers.Free;
  inherited;
end;

initialization

finalization

FreeAndNil(vGLGUIInputManager);

end.
